﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using Co9320.NamaAlert.Engine;
using Co9320.NamaAlert.Model;

namespace Co9320.NamaAlert.Engine.Lists
{
	/// <summary>
	/// 内部リスト｡
	/// コレクション操作系は､スレッドセーフ
	/// 但し､値の参照はノンセーフ(読むことに問題はないが､その時点での最新である保証はない)
	/// </summary>
	internal sealed class InternalLiveList
	{
		private const int Limit = 2;

		private class ValuePair
		{
			private bool OpFlag_;

			public ValuePair(LiveProgram program)
				:this(program,false)
			{
			}

			public ValuePair(LiveProgram program, bool IsInject)
			{
				this.Program = program;
				this.Count = 0;
				OpFlag_ = false;

				this.IsInject = IsInject;
			}


			/// <summary>
			/// 外挿された放送情報か否か
			/// </summary>
			public bool IsInject
			{
				get;
				set;
			}

			/// <summary>
			/// 保持している放送情報
			/// </summary>
			public LiveProgram Program
			{
				get;
				private set;
			}

			/// <summary>
			/// 存在しなかったカウント
			/// </summary>
			public int Count
			{
				get;
				private set;
			}

			/// <summary>
			/// カウンタのインクリメントメソッド
			/// </summary>
			public void Incliment()
			{
				Count++;
				OpFlag_ = true;
			}

			/// <summary>
			/// カウンタのリセットメソッド
			/// </summary>
			public void Reset()
			{
				if (OpFlag_)
				{
					OpFlag_ = false;
				}
				else
				{
					Count = 0;
				}
			}

		}

		private Dictionary<int, ValuePair> Dict_;

		private readonly object SyncRoot_ = new object();


		public InternalLiveList(LiveProgramNotification Parent)
		{
#if !STANDALONE
			Debug.Assert(Parent != null);
#endif


			Dict_ = new Dictionary<int, ValuePair>();
			CommunityFastestTime = DateTime.MinValue;
			this.Parent = Parent;

		}

		public LiveProgramNotification Parent
		{
			get;
			private set;
		}

		public DateTime CommunityFastestTime
		{
			get;
			private set;
		}

		public double Inspect(FeedingLivePrograms InspectionTarget)
		{
			Debug.Assert(InspectionTarget != null);
			Debug.Assert(InspectionTarget.CommunityLiveFastest != null);


			List<StateChangedEventArgs> OnAirList = new List<StateChangedEventArgs>();
			List<StateChangedEventArgs> UpdateList = new List<StateChangedEventArgs>();

			int Total = 0;
			int Duplicate = 0;

			lock (SyncRoot_)
			{
				for (int i = 0; i < InspectionTarget.Count; i++)
				{
					if (Dict_.ContainsKey(InspectionTarget[i].Id))
					{
						var elem = Dict_[InspectionTarget[i].Id];

						if (!elem.IsInject)
						{
							Duplicate++;
						}
						else
						{
							elem.IsInject = false;
						}

						UpdateList.Add(new StateChangedEventArgs(InspectionResult.Exists, InspectionTarget[i]));
						Dict_[InspectionTarget[i].Id].Reset();
					}
					else
					{
						Dict_.Add(InspectionTarget[i].Id, new ValuePair(InspectionTarget[i]));
						OnAirList.Add(new StateChangedEventArgs(InspectionResult.DetectOnAir, InspectionTarget[i]));


						if (this.CommunityFastestTime < InspectionTarget[i].StartDateTime)
						{
							this.CommunityFastestTime = InspectionTarget[i].StartDateTime;
						}
					}
					Total++;
				}
			}

			if (OnAirList.Count != 0)
			{
				Parent.SendStateChanged(OnAirList);
				Parent.SendStateChanged(UpdateList);
			}

			if (Duplicate == 0)
			{
				return 0.0d;
			}
			else
			{
				return (double)Duplicate / (double)Total;
			}

		}

		public int Count
		{
			get
			{
				return Dict_.Count;
			}
		}


		public void Inspect(IInspectionList InspectionTarget)
		{
			Debug.Assert(InspectionTarget != null);

			lock (SyncRoot_)
			{
				List<StateChangedEventArgs> EventList = new List<StateChangedEventArgs>();

				foreach (var elem in Dict_)
				{
					InspectionResult Result = InspectionTarget.Inspect(elem.Value.Program);
					Debug.Assert(Result != InspectionResult.DetectOnAir);

					if (Result == InspectionResult.DetectOffAir)
					{
						if (elem.Value.Count >= Limit)
						{
							EventList.Add(new StateChangedEventArgs(InspectionResult.DetectOffAir, elem.Value.Program));
						}
						else
						{
							elem.Value.Incliment();
						}
					}
				}

				for (int i = 0; i < EventList.Count; i++)
				{
#if DEBUG
					Debug.Assert(Dict_.Remove(EventList[i].Program.Id));
#else
					Dict_.Remove(EventList[i].Program.Id);
#endif
				}


				foreach (var elem in InspectionTarget)
				{
					if (Dict_.ContainsKey(elem.Id))
					{
						EventList.Add(new StateChangedEventArgs(InspectionResult.Exists, elem));
					}
					else
					{
						Dict_.Add(elem.Id, new ValuePair(elem));
						EventList.Add(new StateChangedEventArgs(InspectionResult.DetectOnAir, elem));
					}

				}


				if (EventList.Count != 0)
				{
					Parent.SendStateChanged(EventList);
				}


				foreach (var elem in this.Dict_)
				{
					elem.Value.Reset();
				}
			}

		}

		public bool AddProgram(LiveProgram Program,bool injectFlag)
		{
			Debug.Assert(Program != null);

			lock (SyncRoot_)
			{
				if (Dict_.ContainsKey(Program.Id))
				{
					return false;
				}
				else
				{
					Dict_.Add(Program.Id, new ValuePair(Program, injectFlag));
					if (this.CommunityFastestTime < Program.StartDateTime)
					{
						this.CommunityFastestTime = Program.StartDateTime;
					}
					return true;
				}
			}
		}


		internal void Inspect()
		{
			DateTime Piv = this.CommunityFastestTime;

			if (Piv == DateTime.MinValue)
			{
				return;
			}

			Piv -= new TimeSpan(0, 31, 0);

			List<int> del = new List<int>();

			lock (SyncRoot_)
			{
				foreach (var element in Dict_)
				{
					if (element.Value.Program.StartDateTime < Piv)
					{
						if (element.Value.Program.Station.Kind == StationKind.Community)
						{
							del.Add(element.Key);
						}
						else
						{
							if (element.Value.Count > Limit)
							{
								del.Add(element.Key);
							}
							else
							{
								element.Value.Incliment();
							}
						}
					}
				}

				for (int i = 0; i < del.Count; i++)
				{
					Dict_.Remove(del[i]);
				}
			}
		}
	}
}

